package com.zyb.ext.transaction.aop;

import com.zyb.ext.transaction.annotation.MyTransactionAnnotation;
import com.zyb.ext.transaction.transaction.TransactionUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 注解 - 声明式事务AOP
 * 实现技术：反射 + AOP + 编程式事务
 */
@Slf4j
@Aspect     //标注为切面类
@Component  //给spring管理
public class TransactionAnnotationAop {

    @Autowired
    private TransactionUtils transactionUtils;

    @Around("pointcut()")
    public void around(ProceedingJoinPoint joinpoint) throws Throwable {
        //1、获取代理对象的方法
        Method method = getMethod(joinpoint);
        //2、获取该方法上是否加上了自定义事务注解
        MyTransactionAnnotation declaredAnnotation = method.getAnnotation(MyTransactionAnnotation.class);
        //3、如果存在注解，则开启事务
        beginTransaction(declaredAnnotation);
        //4、执行目标代理对象方法
        joinpoint.proceed();
        //5、判断是否已经开启了事务,开启了事务就提交
        commitTransaction();
    }

    private void commitTransaction() {
        if (transactionUtils.getTransactionStatus() != null) {
            log.info("@Around 环绕通知后提交事务");
            transactionUtils.commit();
        }
    }

    private void beginTransaction(MyTransactionAnnotation declaredAnnotation) {
        if (declaredAnnotation != null) {
            log.info("@Around 环绕通知前开启事务");
            transactionUtils.begin();
        }
    }

    private Method getMethod(ProceedingJoinPoint joinpoint) throws NoSuchMethodException {
        String methodName = joinpoint.getSignature().getName();
        Class<?> classTarget = joinpoint.getTarget().getClass();
        Class[] parameterTypes = ((MethodSignature) joinpoint.getSignature()).getParameterTypes();
        return classTarget.getMethod(methodName, parameterTypes);
    }

    @AfterThrowing(value = "pointcut()", throwing = "ex")
    public void afterThrowing(Exception ex) {
        if (transactionUtils.getTransactionStatus() != null) {
            log.info("@AfterThrowing 抛出异常,执行事务的回滚");
            //获取当前事务对象，并回滚事务
            transactionUtils.rollback();
        }
    }

    @Pointcut("execution(* com.zyb.ext.transaction.service.impl.UserServiceImpl.add(..))")
    public void pointcut() {
    }
}
